package com.quanz.ckgl.services.db;

import java.util.ArrayList;
import java.util.List;

import javax.annotation.Resource;

import org.apache.commons.lang3.StringUtils;
import org.crank.crud.CriteriaUtils;
import org.crank.crud.criteria.Criterion;
import org.crank.crud.criteria.OrderBy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import com.quanz.ckgl.services.WarehouseWarrant;
import com.quanz.ckgl.services.DeliveryOrder;
import com.quanz.ckgl.services.Stock;
import com.quanz.ckgl.services.StockService;
import com.quanz.ckgl.services.dao.DeliveryOrderDao;
import com.quanz.ckgl.services.dao.ProductDao;
import com.quanz.ckgl.services.dao.RackDao;
import com.quanz.ckgl.services.dao.StockDao;
import com.quanz.ckgl.services.dao.UserDao;
import com.quanz.ckgl.services.dao.WarehouseWarrantDao;
import com.quanz.ckgl.services.entity.DeliveryOrderEntity;
import com.quanz.ckgl.services.entity.ProductEntity;
import com.quanz.ckgl.services.entity.RackEntity;
import com.quanz.ckgl.services.entity.StockEntity;
import com.quanz.ckgl.services.entity.UserEntity;
import com.quanz.ckgl.services.entity.WarehouseWarrantEntity;

/**
 * 基于数据库的{@link StockService}实现
 * 
 * @author James Quan
 * @version 2015年10月12日 下午10:18:52
 */
@Component
public class DbStockService implements StockService {
	private Logger logger = LoggerFactory.getLogger(getClass());
	private StockDao stockDao;
	private WarehouseWarrantDao wvDao;
	private DeliveryOrderDao dvDao;
	private ProductDao productDao;
	private RackDao rackDao;
	private UserDao userDao;

	public StockDao getStockDao() {
		return stockDao;
	}

	@Resource
	public void setStockDao(StockDao productDao) {
		this.stockDao = productDao;
	}

	public WarehouseWarrantDao getWvDao() {
		return wvDao;
	}

	@Resource
	public void setWvDao(WarehouseWarrantDao wvDao) {
		this.wvDao = wvDao;
	}

	public DeliveryOrderDao getDvDao() {
		return dvDao;
	}

	@Resource
	public void setDvDao(DeliveryOrderDao dvDao) {
		this.dvDao = dvDao;
	}

	public ProductDao getProductDao() {
		return productDao;
	}

	@Resource
	public void setProductDao(ProductDao productDao) {
		this.productDao = productDao;
	}

	public RackDao getRackDao() {
		return rackDao;
	}

	@Resource
	public void setRackDao(RackDao rackDao) {
		this.rackDao = rackDao;
	}

	public UserDao getUserDao() {
		return userDao;
	}
	
	@Resource
	public void setUserDao(UserDao userDao) {
		this.userDao = userDao;
	}
	
	@Override
	@Transactional(readOnly = true)
	public Stock[] findStock(OrderBy[] orderBy, int startPosition,
			int maxResults, Criterion... criteria) {
		Specification<StockEntity> spec = CriteriaUtils.getSpecification(
				StockEntity.class, criteria);
		Sort sort = CriteriaUtils.getSort(orderBy);
		List<StockEntity> entities = stockDao.findList(spec, startPosition,
				maxResults, sort);
		List<Stock> stocks = new ArrayList<Stock>(entities.size());
		for (StockEntity entity : entities) {
			Stock product = wrap(entity);
			stocks.add(product);
		}
		return stocks.toArray(new Stock[stocks.size()]);
	}

	@Override
	@Transactional(readOnly = true)
	public int findStockCount(Criterion... criteria) {
		Specification<StockEntity> spec = CriteriaUtils.getSpecification(
				StockEntity.class, criteria);
		long c = stockDao.count(spec);
		return (int) c;
	}

	@Override
	@Transactional(readOnly = true)
	public Stock findStockById(long id) {
		StockEntity entity = stockDao.findOne(id);
		return wrap(entity);
	}

	@Override
	@Transactional
	public Stock createStock(Stock stock) {
		StockEntity entity = new StockEntity();
		ProductEntity product = productDao.findOne(stock.getProductId());
		if (product == null) {
			throw new IllegalArgumentException("product "
					+ stock.getProductId() + " not found!");
		}
		RackEntity rack = rackDao.findOne(stock.getRackId());
		if (rack == null) {
			throw new IllegalArgumentException("rack " + stock.getRackId()
					+ " not found!");
		}
		entity.setProduct(product);
		entity.setRack(rack);
		entity.setLayer(stock.getLayer());
		entity.setCol(stock.getCol());
		entity.setQuantity(stock.getQuantity());
		entity.setReceiveTime(stock.getReceiveTime());
		stockDao.persist(entity);
		return wrap(entity);
	}

	@Override
	@Transactional
	public synchronized void updateStock(Stock stock) {
		StockEntity entity = stockDao.findOne(stock.getId());
		if (entity == null) {
			throw new IllegalArgumentException("stock " + stock.getId()
					+ " not found!");
		}
		ProductEntity product = productDao.findOne(stock.getProductId());
		if (product == null) {
			throw new IllegalArgumentException("product "
					+ stock.getProductId() + " not found!");
		}
		RackEntity rack = rackDao.findOne(stock.getRackId());
		if (rack == null) {
			throw new IllegalArgumentException("rack " + stock.getRackId()
					+ " not found!");
		}
		entity.setProduct(product);
		entity.setRack(rack);
		entity.setLayer(stock.getLayer());
		entity.setCol(stock.getCol());
		entity.setQuantity(stock.getQuantity());
		entity.setReceiveTime(stock.getReceiveTime());
		entity = stockDao.merge(entity);
	}

	@Override
	@Transactional
	public synchronized void removeStock(Stock stock) {
		StockEntity entity = stockDao.findOne(stock.getId());
		if (entity == null) {
			return;
		}
		stockDao.delete(entity);
	}

	@Override
	@Transactional(readOnly = true)
	public WarehouseWarrant[] findWV(OrderBy[] orderBy, int startPosition,
			int maxResults, Criterion... criteria) {
		Specification<WarehouseWarrantEntity> spec = CriteriaUtils
				.getSpecification(WarehouseWarrantEntity.class, criteria);
		Sort sort = CriteriaUtils.getSort(orderBy);
		List<WarehouseWarrantEntity> entities = wvDao.findList(spec,
				startPosition, maxResults, sort);
		List<WarehouseWarrant> vouchers = new ArrayList<WarehouseWarrant>(
				entities.size());
		for (WarehouseWarrantEntity entity : entities) {
			WarehouseWarrant product = wrap(entity);
			vouchers.add(product);
		}
		return vouchers.toArray(new WarehouseWarrant[vouchers.size()]);
	}

	@Override
	@Transactional(readOnly = true)
	public int findWVCount(Criterion... criteria) {
		Specification<WarehouseWarrantEntity> spec = CriteriaUtils
				.getSpecification(WarehouseWarrantEntity.class, criteria);
		long c = wvDao.count(spec);
		return (int) c;
	}

	@Override
	@Transactional
	public synchronized WarehouseWarrant stockIn(WarehouseWarrant voucher) {
		String rackId = voucher.getRackId();
		int layer = voucher.getLayer();
		int col = voucher.getCol();
		String productId = voucher.getProductId();
		String barcode = voucher.getBarcode();
		long userId = voucher.getUserId();
		ProductEntity product = null;
		if(!StringUtils.isEmpty(productId)){
			product = productDao.findOne(productId);
		}
		if(product == null && !StringUtils.isEmpty(barcode)) {
			product = productDao.findByBarcode(barcode);
		}
		if (product == null) {
			throw new IllegalArgumentException("product " + productId
					+ " not found!");
		}
		RackEntity rack = rackDao.findOne(rackId);
		if (rack == null) {
			throw new IllegalArgumentException("rack " + rackId + " not found!");
		}
		UserEntity user = userDao.findOne(userId);
		if (user == null) {
			throw new IllegalArgumentException("user " + rackId + " not found!");
		}
		WarehouseWarrantEntity wvEntity = new WarehouseWarrantEntity();
		wvEntity.setProduct(product);
		wvEntity.setRack(rack);
		wvEntity.setLayer(layer);
		wvEntity.setCol(col);
		wvEntity.setQuantity(voucher.getQuantity());
		wvEntity.setStorageTime(voucher.getStorageTime());
		wvEntity.setUser(user);
		wvDao.persist(wvEntity);

		StockEntity entity = stockDao.findByRackIdAndLayerAndColAndProductId(rackId,
				layer, col, productId);
		if (entity == null) {
			entity = new StockEntity();
			entity.setProduct(product);
			entity.setRack(rack);
			entity.setLayer(layer);
			entity.setCol(col);
			entity.setQuantity(voucher.getQuantity());
			entity.setReceiveTime(voucher.getStorageTime());
			stockDao.persist(entity);
		} else {
			entity.setQuantity(entity.getQuantity() + voucher.getQuantity());
			entity.setReceiveTime(voucher.getStorageTime());
			stockDao.merge(entity);
		}
		return wrap(wvEntity);
	}

	@Override
	@Transactional
	public synchronized WarehouseWarrant[] stockIn(WarehouseWarrant[] vouchers) {
		if(vouchers == null || vouchers.length == 0) {
			return new WarehouseWarrant[0];
		}
		WarehouseWarrant[] result = new WarehouseWarrant[vouchers.length];
		for(int i = 0; i < vouchers.length; i++) {
			WarehouseWarrant voucher = vouchers[i];
			result[i] = stockIn(voucher);
		}
		return result;
	}
	
	@Override
	@Transactional(readOnly = true)
	public DeliveryOrder[] findDV(OrderBy[] orderBy, int startPosition,
			int maxResults, Criterion... criteria) {
		Specification<DeliveryOrderEntity> spec = CriteriaUtils
				.getSpecification(DeliveryOrderEntity.class, criteria);
		Sort sort = CriteriaUtils.getSort(orderBy);
		List<DeliveryOrderEntity> entities = dvDao.findList(spec,
				startPosition, maxResults, sort);
		List<DeliveryOrder> vouchers = new ArrayList<DeliveryOrder>(
				entities.size());
		for (DeliveryOrderEntity entity : entities) {
			DeliveryOrder product = wrap(entity);
			vouchers.add(product);
		}
		return vouchers.toArray(new DeliveryOrder[vouchers.size()]);
	}

	@Override
	@Transactional(readOnly = true)
	public int findDVCount(Criterion... criteria) {
		Specification<DeliveryOrderEntity> spec = CriteriaUtils
				.getSpecification(DeliveryOrderEntity.class, criteria);
		long c = dvDao.count(spec);
		return (int) c;
	}

	@Override
	@Transactional
	public synchronized DeliveryOrder stockOut(DeliveryOrder voucher) {
		String rackId = voucher.getRackId();
		int layer = voucher.getLayer();
		int col = voucher.getCol();
		String productId = voucher.getProductId();
		long userId = voucher.getUserId();
		StockEntity entity = stockDao.findByRackIdAndLayerAndColAndProductId(rackId,
				layer, col, productId);
		if (entity == null) {
			throw new IllegalArgumentException("stock not found, product: "
					+ productId + ", rack: " + rackId + ", layer: " + layer + ", col"
					+ col);
		}
		UserEntity user = userDao.findOne(userId);
		if (user == null) {
			throw new IllegalArgumentException("user " + rackId + " not found!");
		}
		
		if (entity.getQuantity() < voucher.getQuantity()) {
			throw new IllegalArgumentException("not enough stock");
		} else {
			int quantity = entity.getQuantity() - voucher.getQuantity();
			if (quantity == 0) {
				stockDao.delete(entity);
			} else {
				entity.setQuantity(entity.getQuantity() - voucher.getQuantity());
				entity = stockDao.merge(entity);
			}
		}

		DeliveryOrderEntity dvEntity = new DeliveryOrderEntity();
		dvEntity.setProduct(entity.getProduct());
		dvEntity.setRack(entity.getRack());
		dvEntity.setLayer(entity.getLayer());
		dvEntity.setCol(entity.getCol());
		dvEntity.setQuantity(voucher.getQuantity());
		dvEntity.setDeliveryTime(voucher.getDeliveryTime());
		dvEntity.setUser(user);
		dvDao.persist(dvEntity);
		return wrap(dvEntity);
	}

	@Override
	@Transactional
	public synchronized DeliveryOrder[] stockOut(DeliveryOrder[] vouchers) {
		if(vouchers == null || vouchers.length == 0) {
			return new DeliveryOrder[0];
		}
		DeliveryOrder[] result = new DeliveryOrder[vouchers.length];
		for(int i = 0; i < vouchers.length; i++) {
			DeliveryOrder voucher = vouchers[i];
			result[i] = stockOut(voucher);
		}
		return result;
	}
	
	protected Stock wrap(StockEntity entity) {
		if (entity == null) {
			return null;
		}
		Stock stock = new Stock();
		stock.setId(entity.getId());
		stock.setProductId(entity.getProduct().getId());
		stock.setProductName(entity.getProduct().getName());
		stock.setVendor(entity.getProduct().getVendor());
		stock.setPhone(entity.getProduct().getPhone());
		stock.setRackId(entity.getRack().getId());
		stock.setRackNote(entity.getRack().getNote());
		stock.setLayer(entity.getLayer());
		stock.setCol(entity.getCol());
		stock.setQuantity(entity.getQuantity());
		stock.setReceiveTime(entity.getReceiveTime());
		return stock;
	}

	protected WarehouseWarrant wrap(WarehouseWarrantEntity entity) {
		if (entity == null) {
			return null;
		}
		WarehouseWarrant voucher = new WarehouseWarrant();
		voucher.setId(entity.getId());
		voucher.setProductId(entity.getProduct().getId());
		voucher.setBarcode(entity.getProduct().getBarcode());
		voucher.setProductName(entity.getProduct().getName());
		voucher.setVendor(entity.getProduct().getVendor());
		voucher.setPhone(entity.getProduct().getPhone());
		voucher.setRackId(entity.getRack().getId());
		voucher.setRackNote(entity.getRack().getNote());
		voucher.setLayer(entity.getLayer());
		voucher.setCol(entity.getCol());
		voucher.setQuantity(entity.getQuantity());
		voucher.setStorageTime(entity.getStorageTime());
		voucher.setUserId(entity.getUser().getId());
		voucher.setUserName(entity.getUser().getName());
		return voucher;
	}
	
	protected DeliveryOrder wrap(DeliveryOrderEntity entity) {
		if (entity == null) {
			return null;
		}
		DeliveryOrder voucher = new DeliveryOrder();
		voucher.setId(entity.getId());
		voucher.setProductId(entity.getProduct().getId());
		voucher.setProductName(entity.getProduct().getName());
		voucher.setVendor(entity.getProduct().getVendor());
		voucher.setPhone(entity.getProduct().getPhone());
		voucher.setRackId(entity.getRack().getId());
		voucher.setRackNote(entity.getRack().getNote());
		voucher.setLayer(entity.getLayer());
		voucher.setCol(entity.getCol());
		voucher.setQuantity(entity.getQuantity());
		voucher.setDeliveryTime(entity.getDeliveryTime());
		voucher.setUserId(entity.getUser().getId());
		voucher.setUserName(entity.getUser().getName());
		return voucher;
	}
}
